#pragma once

// a json read/write library 

#include <cassert>
#include <memory>
#include <list>
#include "Output/Console.h"
#include "Math/Math.h" //for pow

#include "Containers/Map.h"
#include "Containers/Vector.h"


class CJsonVectorNode; //list
class CJsonObjectNode; //dict

class CJsonIntNode; 
class CJsonFloatNode; 
class CJsonStringNode; //string
class CJsonBoolNode; //bool
class CJsonNullNode; //null

class CJsonNodeBase : public std::enable_shared_from_this<CJsonNodeBase>
{

public:
	virtual ~CJsonNodeBase() = default;

	//is a null node
	virtual bool IsNull()
	{
		return false;
	}

	std::shared_ptr<CJsonNullNode> ToNull()
	{
		assert(IsNull() && "this is not a null node");
		return std::static_pointer_cast<CJsonNullNode>(shared_from_this());
	}

	//is a bool node
	virtual bool IsBool()
	{
		return false;
	}

	std::shared_ptr<CJsonBoolNode> ToBool()
	{
		assert(IsBool() && "this is not a bool node");
		return std::static_pointer_cast<CJsonBoolNode>(shared_from_this());
	}

	//is a int node
	virtual bool IsInt()
	{
		return false;
	}

	std::shared_ptr<CJsonIntNode> ToInt()
	{
		assert(IsInt() && "this is not a int node");
		return std::static_pointer_cast<CJsonIntNode>(shared_from_this());
	}

	//is a float node
	virtual bool IsFloat()
	{
		return false;
	}

	std::shared_ptr<CJsonFloatNode> ToFloat()
	{
		assert(IsFloat() && "this is not a float node");
		return std::static_pointer_cast<CJsonFloatNode>(shared_from_this());
	}

	//is a string node
	virtual bool IsString()
	{
		return false;
	}

	std::shared_ptr<CJsonStringNode> ToString()
	{
		assert(IsString() && "this is not a string node");
		return std::static_pointer_cast<CJsonStringNode>(shared_from_this());
	}

	//is a list node
	virtual bool IsList()
	{
		return false;
	}

	std::shared_ptr<CJsonVectorNode> ToList()
	{
		assert(IsList() && "this is not a list node");
		return std::static_pointer_cast<CJsonVectorNode>(shared_from_this());
	}

	//is a Object node
	virtual bool IsObject()
	{
		return false;
	}

	std::shared_ptr<CJsonObjectNode> ToObject()
	{
		assert(IsObject() && "this is not a Object node");
		return std::static_pointer_cast<CJsonObjectNode>(shared_from_this());
	}

	//@escape char, if true, will escape \n \t \r \b \f \", this is used for network transfer
	virtual void ToString(std::list<CString>& OutString
		, int InLevel = 0
		,bool bPretty=true
		) = 0;
};

class CJsonNullNode : public CJsonNodeBase
{
public:
	bool IsNull() override
	{
		return true;
	}

	void ToString(std::list<CString>& OutString
		, int InLevel = 0
		, bool bPretty = true
	) override
	{
		OutString.push_back(U"null");
	}
};

class CJsonBoolNode : public CJsonNodeBase
{
public:
	CJsonBoolNode()=default;

	CJsonBoolNode(bool InValue)
	{
		Value = InValue;
	}

	bool IsBool() override
	{
		return true;
	}

	bool GetValue() const
	{
		return Value;
	}

	void SetValue(bool InValue)
	{
		Value = InValue;
	}

	void ToString(std::list<CString>& OutString
		, int InLevel = 0
		, bool bPretty = true
	) override
	{
		OutString.push_back(Value ? U"true" : U"false");
	}

private:
	bool Value = false;
};

class CJsonIntNode : public CJsonNodeBase
{
public:
	CJsonIntNode()=default;

	CJsonIntNode(int64_t InValue)
	{
		Value = InValue;
	}

	bool IsInt() override
	{
		return true;
	}

	int64_t GetValue() const
	{
		return Value;
	}

	void SetValue(int64_t InValue)
	{
		Value = InValue;
	}

	void ToString(std::list<CString>& OutString
		, int InLevel = 0
		, bool bPretty = true
	) override
	{
		OutString.push_back(CString(Value));
	}

private:
	int64_t Value = 0;
};

class CJsonFloatNode : public CJsonNodeBase
{
public:
	CJsonFloatNode()=default;

	CJsonFloatNode(double InValue)
	{
		Value = InValue;
	}

	bool IsFloat() override
	{
		return true;
	}

	double GetValue() const
	{
		return Value;
	}

	void SetValue(double InValue)
	{
		Value = InValue;
	}

	void ToString(std::list<CString>& OutString
		, int InLevel = 0
		, bool bPretty = true
	) override
	{
		OutString.push_back(CString(Value));
	}

private:
	double Value = 0;
};

class CJsonStringNode : public CJsonNodeBase
{
public:
	CJsonStringNode() = default;

	CJsonStringNode(const CString& InString)
	{
		Value =InString;
	}

	bool IsString() override
	{
		return true;
	}

	const CString& GetValue() const
	{
		return Value;
	}

	void SetValue(const CString& InValue)
	{
		Value = InValue;
	}

	void ToString(std::list<CString>& OutString
		, int InLevel = 0
		, bool bPretty = true
	) override
	{
		OutString.push_back(U"\"");
		OutString.push_back(ConvertEscapeChar(Value));
		OutString.push_back(U"\"");

	}

	//convert escape char to json char
	//json escape char : \ \n \t \r \b \f \"

	static CString ConvertEscapeChar(const CString& InString)
	{
		TVector<char32_t> Result;
		Result.Reserve(InString.GetLength()*2+1);

		for (int64_t i = 0; i < InString.GetLength(); ++i)
		{
			char32_t Char = InString[i];
			switch (Char)
			{
				case U'\\':
				{
					Result.Add(U'\\');
					Result.Add(U'\\');
					break;
				}
				case U'\n':
				{
					Result.Add(U'\\');
					Result.Add(U'n');
					break;
				}
				case U'\t':
				{
					Result.Add(U'\\');
					Result.Add(U't');
					break;
				}
				case U'\r':
				{
					Result.Add(U'\\');
					Result.Add(U'r');
					break;
				}
				case U'\b':
				{
					Result.Add(U'\\');
					Result.Add(U'b');
					break;
				}
				case U'\f':
				{
					Result.Add(U'\\');
					Result.Add(U'f');
					break;
				}
				case U'\"':
				{	
					Result.Add(U'\\');
					Result.Add(U'\"');
					break;
				}
				default:
				{
					Result.Add(Char);
					break;
				}
			}
		}

		Result.Add(U'\0');

		return CString(Result.Data());
	
	}

	//convert json char to escape char
	//json escape char : \n \t \r \b \f \"
	static CString ConvertJsonChar(const CString& InString)
	{
		TVector<char32_t> Result;
		Result.Reserve(InString.GetLength() + 1);

		for (int64_t i = 0; i < InString.GetLength(); ++i)
		{
			char32_t Char = InString[i];
			if(Char==U'\\' && (i+1)<InString.GetLength())
			{	
				char32_t NextChar=InString[i+1];
				switch (NextChar)
				{
					case U'\\':
					{
						Result.Add(U'\\');
						break;
					}
					case U'n':
					{
						Result.Add(U'\n');
						break;
					}
					case U't':
					{
						Result.Add(U'\t');
						break;
					}
					case U'r':
					{
						Result.Add(U'\r');
						break;
					}
					case U'b':
					{
						Result.Add(U'\b');
						break;
					}
					case U'f':
					{
						Result.Add(U'\f');
						break;
					}
					case U'\"':
					{
						Result.Add(U'\"');
						break;
					}
					default:
					{
						Result.Add(Char);
						continue;
						break;
					}
				}
				i++;
			}
			else
			{
				Result.Add(Char);
			}
		}

		Result.Add(U'\0');

		return CString(Result.Data());
	}

private:


	CString Value;
};

class CJsonVectorNode : public CJsonNodeBase
{
public:
	bool IsList() override
	{
		return true;
	}

	void Add(std::shared_ptr<CJsonNodeBase> InNode)
	{
		Nodes.Add(InNode);
	}

	void Remove(std::shared_ptr<CJsonNodeBase> InNode)
	{
		Nodes.Remove(InNode);
	}

	void RemoveAt(int64_t InIndex)
	{
		Nodes.RemoveAt(InIndex);
	}

	void Clear()
	{
		Nodes.Clear();
	}

	int64_t Num() const
	{
		return Nodes.Num();
	}

	std::shared_ptr<CJsonNodeBase> Get(int64_t InIndex)
	{
		return Nodes[InIndex];
	}

	TVector<std::shared_ptr<CJsonNodeBase>>& GetNodes()
	{
		return Nodes;
	}

	void ToString(std::list<CString>& OutString
		, int InLevel = 0
		, bool bPretty = true
	) override
	{
		if(bPretty)
		{
			if(InLevel>0)
			{
				OutString.emplace_back(CString(U"\n"));
			}

			OutString.emplace_back(CString(U"\t").Repeat(InLevel));
			OutString.emplace_back(U"[\n");

			CString Space=CString(U"\t").Repeat(InLevel+1);
			for (int64_t i = 0; i < Nodes.Num(); ++i)
			{
				OutString.emplace_back(Space);
				Nodes[i]->ToString(OutString, InLevel + 1, bPretty);
				if (i != Nodes.Num() - 1)
				{
					OutString.push_back(U",\n");
				}
				else
				{
					OutString.push_back(U"\n");
				}
			}
			OutString.emplace_back(CString(U"\t").Repeat(InLevel));
			OutString.emplace_back(U"]");
		}
		else
		{
			OutString.emplace_back(U"[");
			for (int64_t i = 0; i < Nodes.Num(); ++i)
			{
				Nodes[i]->ToString(OutString, InLevel + 1, bPretty);
				if (i != Nodes.Num() - 1)
				{
					OutString.push_back(U",");
				}
			}
			OutString.emplace_back(U"]");
		}
	}

private:
	TVector<std::shared_ptr<CJsonNodeBase>> Nodes;
};

class CJsonObjectNode : public CJsonNodeBase
{
private:
	TMap<CString, std::shared_ptr<CJsonNodeBase>> Nodes;

public:
	bool IsObject() override
	{
		return true;
	}

	void Add(const CString& InKey, std::shared_ptr<CJsonNodeBase> InNode)
	{
		Nodes.Add(InKey, InNode);
	}

	void Remove(const CString& InKey)
	{
		Nodes.Remove(InKey);
	}

	void Clear()
	{
		Nodes.Clear();
	}

	int64_t Num() const
	{
		return Nodes.Num();
	}

	bool Contains(const CString& InKey)
	{
		return Nodes.Contains(InKey);
	}

	std::shared_ptr<CJsonNodeBase> Get(const CString& InKey)
	{
		if(!Nodes.Contains(InKey))
		{
			return nullptr;
		}

		return Nodes[InKey];
	}

	void ToString(std::list<CString>& OutString
		, int InLevel = 0
		, bool bPretty = true
	) override
	{
		
		if (bPretty)
		{
			if(InLevel>0)
			{
				OutString.emplace_back(CString(U"\n"));
			}

			OutString.emplace_back(CString(U"\t").Repeat(InLevel));
			OutString.emplace_back(U"{\n");
			int64_t i = 0;
			for (auto& Pair : Nodes)
			{
				OutString.emplace_back(CString(U"\t").Repeat(InLevel + 1));
				OutString.push_back(U"\"");
				OutString.push_back(Pair.Key);
				OutString.push_back(U"\":");
				Pair.Value->ToString(OutString, InLevel + 1, bPretty);
				if (i != Nodes.Num() - 1)
				{
					OutString.push_back(U",\n");
				}
				else
				{
					OutString.push_back(U"\n");
				}
				++i;
			}
			OutString.emplace_back(CString(U"\t").Repeat(InLevel));
			OutString.emplace_back(U"}");
		}
		else
		{
			OutString.emplace_back(U"{");
			int64_t i = 0;
			for (auto& Pair : Nodes)
			{
				OutString.push_back(U"\"");
				OutString.push_back(Pair.Key);
				OutString.push_back(U"\":");
				Pair.Value->ToString(OutString, InLevel + 1, bPretty);
				if (i != Nodes.Num() - 1)
				{
					OutString.push_back(U",");
				}
				++i;
			}
			OutString.emplace_back(U"}");
		}
	}

};

class CJsonUtils
{
public:
	static std::shared_ptr<CJsonNodeBase> Parse(const CString& InString)
	{
		//parse into tokens
		TVector<SToken> Tokens;

		//guess token count
		Tokens.Reserve(InString.GetLength()/3);

		auto Error=ParseTokens(InString, Tokens);
		if(!Error.IsEmpty())
		{
			goto ParseError;
		}

		if(Tokens.Empty())
		{
			Error = U"empty string";
			goto ParseError;
		}

		//parse tokens into json node
		{	
			std::shared_ptr<CJsonNodeBase> RootNode;
			TVector<std::shared_ptr<CJsonNodeBase>> NodeStack; //only store list and object node
		
			CString CurrentKey;
			bool bNextStringCanBeKey = false;

			for (int64_t TokenIndex = 0; TokenIndex < Tokens.Num(); )
			{
				auto Token = Tokens[TokenIndex];
				switch (Token.Type)
				{
					case CJsonUtils::SToken::EType::LeftBrace:
					{
						//this is a object
						auto Node = std::make_shared<CJsonObjectNode>();
						if(NodeStack.Empty())
						{
							if(RootNode)
							{
								Error =WH::Format(
									U"unexpected token at :Line {} Cloumn {}"
									,Token.LineIndex
									,Token.ColumnIndex
								);
								goto ParseError;
							}

							RootNode = Node;
						}
						else if (NodeStack.Last()->IsList())
						{
							NodeStack.Last()->ToList()->Add(Node);
						}
						else if (NodeStack.Last()->IsObject())
						{
							NodeStack.Last()->ToObject()->Add(CurrentKey, Node);
						}
						else
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}
						NodeStack.Add(Node);

						//skip '{'
						TokenIndex++;

						bNextStringCanBeKey=true;
						
						break;

					}

					case CJsonUtils::SToken::EType::RightBrace:
					{
						if (NodeStack.Empty())
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						if (!NodeStack.Last()->IsObject())
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						NodeStack.Pop();

						//skip '}'
						TokenIndex++;

						bNextStringCanBeKey=false;

						break;
					}

					case CJsonUtils::SToken::EType::LeftBracket:
					{
						//this is a list
						auto Node = std::make_shared<CJsonVectorNode>();
						if (NodeStack.Empty())
						{
							if (RootNode)
							{
								Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
								goto ParseError;
							}

							RootNode = Node;
						}
						else if (NodeStack.Last()->IsList())
						{
							NodeStack.Last()->ToList()->Add(Node);
						}
						else if (NodeStack.Last()->IsObject())
						{
							NodeStack.Last()->ToObject()->Add(CurrentKey, Node);
						}
						else
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}
						NodeStack.Add(Node);

						//skip '['
						TokenIndex++;

						bNextStringCanBeKey=false;

						break;
					}
				
					case CJsonUtils::SToken::EType::RightBracket:
					{
						if (NodeStack.Empty())
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						if (!NodeStack.Last()->IsList())
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						NodeStack.Pop();

						//skip ']'
						TokenIndex++;

						bNextStringCanBeKey=false;

						break;
					}

					case CJsonUtils::SToken::EType::String:
					{
						if(bNextStringCanBeKey)
						{
							CurrentKey=Token.StringValue;
							
							//skip string
							TokenIndex++;

							//skip ':'
							if(TokenIndex>=Tokens.Num() || Tokens[TokenIndex].Type!=CJsonUtils::SToken::EType::Colon)
							{
								Error = WH::Format(U"expected a ':' at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
								goto ParseError;
							}
							TokenIndex++;

							bNextStringCanBeKey=false;

							break;
						}

						//this is a string node
						auto Node = std::make_shared<CJsonStringNode>();
						Node->SetValue(CJsonStringNode::ConvertJsonChar(Token.StringValue));

						if (NodeStack.Empty())
						{
							Error = WH::Format(U"string node must be in a object or list at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}
						else if (NodeStack.Last()->IsList())
						{
							NodeStack.Last()->ToList()->Add(Node);
						}
						else if (NodeStack.Last()->IsObject())
						{
							NodeStack.Last()->ToObject()->Add(CurrentKey, Node);
						}
						else
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						//skip string
						TokenIndex++;
						break;
					}
				
					case CJsonUtils::SToken::EType::Int:
					{
						//this is a int node
						auto Node = std::make_shared<CJsonIntNode>();
						Node->SetValue(Token.IntValue);

						if (NodeStack.Empty())
						{
							Error = WH::Format(U"int node must be in a object or list at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}
						else if (NodeStack.Last()->IsList())
						{
							NodeStack.Last()->ToList()->Add(Node);
						}
						else if (NodeStack.Last()->IsObject())
						{
							NodeStack.Last()->ToObject()->Add(CurrentKey, Node);
						}
						else
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						//skip int
						TokenIndex++;
						break;
					}

					case CJsonUtils::SToken::EType::Float:
					{
						//this is a float node
						auto Node = std::make_shared<CJsonFloatNode>();
						Node->SetValue(Token.FloatValue);

						if (NodeStack.Empty())
						{
							Error = WH::Format(U"float node must be in a object or list at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}
						else if (NodeStack.Last()->IsList())
						{
							NodeStack.Last()->ToList()->Add(Node);
						}
						else if (NodeStack.Last()->IsObject())
						{
							NodeStack.Last()->ToObject()->Add(CurrentKey, Node);
						}
						else
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						//skip float
						TokenIndex++;
						break;
					}

					case CJsonUtils::SToken::EType::Bool:
					{
						//this is a bool node
						auto Node = std::make_shared<CJsonBoolNode>();
						Node->SetValue(Token.BoolValue);
						
						if (NodeStack.Empty())
						{
							Error = WH::Format(U"bool node must be in a object or list at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}
						else if (NodeStack.Last()->IsList())
						{
							NodeStack.Last()->ToList()->Add(Node);
						}
						else if (NodeStack.Last()->IsObject())
						{
							NodeStack.Last()->ToObject()->Add(CurrentKey, Node);
						}
						else
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						//skip bool
						TokenIndex++;
						break;
					}

					case CJsonUtils::SToken::EType::Null:
					{
						//this is a null node
						auto Node = std::make_shared<CJsonNullNode>();

						if (NodeStack.Empty())
						{
							Error = WH::Format(U"null node must be in a object or list at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}
						else if (NodeStack.Last()->IsList())
						{
							NodeStack.Last()->ToList()->Add(Node);
						}
						else if (NodeStack.Last()->IsObject())
						{
							NodeStack.Last()->ToObject()->Add(CurrentKey, Node);
						}
						else
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						//skip null
						TokenIndex++;
						break;
					}

					case CJsonUtils::SToken::EType::Comma:
					{
						if (NodeStack.Empty())
						{
							Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
							goto ParseError;
						}

						//skip ','
						TokenIndex++;

						bNextStringCanBeKey=NodeStack.Last()->IsObject();
						break;
					}

					default :
					{
						Error = WH::Format(U"unexpected token at :Line {} Cloumn {}", Token.LineIndex, Token.ColumnIndex);
						goto ParseError;
					}
				}
			}

			assert(Error.IsEmpty() && "should not reach here");
			assert(RootNode && "should not reach here");
			return RootNode;
		}
	ParseError :	
		WH::PrintLine(U"Parse json error : {}", Error);
		return nullptr;

	}

	static CString ToString(std::shared_ptr<CJsonNodeBase> InNode
		, bool bPretty = true
	)
	{
		std::list<CString> StringList;
		InNode->ToString(StringList, 0, bPretty);
		return CString::Join(StringList);
	}

protected:
	struct SToken
	{
		enum class EType
		{
			Invalid,
			LeftBrace,
			RightBrace,
			LeftBracket,
			RightBracket,
			Colon,
			Comma,
			String,
			Int,
			Float,
			Bool,
			Null,
		};

		EType Type = EType::Invalid;
		
		CString StringValue; //only valid for string
		int64_t IntValue = 0; //only valid for int
		double FloatValue = 0; //only valid for float
		bool BoolValue = false; //only valid for bool

		int64_t LineIndex = 0;
		int64_t ColumnIndex = 0;

		SToken(EType InType, int64_t InLineIndex, int64_t InColumnIndex)
			:Type(InType)
			,LineIndex(InLineIndex)
			,ColumnIndex(InColumnIndex)
		{
		}

		SToken(const CString& InStringValue, int64_t InLineIndex, int64_t InColumnIndex)
			:Type(EType::String)
			,StringValue(InStringValue)
			,LineIndex(InLineIndex)
			,ColumnIndex(InColumnIndex)
		{
		}

		SToken(int64_t InIntValue, int64_t InLineIndex, int64_t InColumnIndex)
			:Type(EType::Int)
			,IntValue(InIntValue)
			,LineIndex(InLineIndex)
			,ColumnIndex(InColumnIndex)
		{
		}

		SToken(double InFloatValue, int64_t InLineIndex, int64_t InColumnIndex)
			:Type(EType::Float)
			,FloatValue(InFloatValue)
			,LineIndex(InLineIndex)
			,ColumnIndex(InColumnIndex)
		{
		}

		SToken(bool InBoolValue, int64_t InLineIndex, int64_t InColumnIndex)
			:Type(EType::Bool)
			,BoolValue(InBoolValue)
			,LineIndex(InLineIndex)
			,ColumnIndex(InColumnIndex)
		{
		}
	};


	//return error
	static CString ParseTokens(const CString& InString,TVector<SToken>& OutTokens)
	{
		auto IsSpace = [](char32_t InChar)
		{
			return InChar == U' ' || InChar == U'\t' || InChar == U'\r' || InChar == U'\n';
		};

		auto IsDigit = [](char32_t InChar)
		{
			return InChar >= U'0' && InChar <= U'9';
		};

		auto IsHex = [](char32_t InChar)
		{
			return (InChar >= U'0' && InChar <= U'9') || (InChar >= U'a' && InChar <= U'f') || (InChar >= U'A' && InChar <= U'F');
		};

		auto IsAlpha = [](char32_t InChar)
		{
			return (InChar >= U'a' && InChar <= U'z') || (InChar >= U'A' && InChar <= U'Z');
		};

		auto IsAlphaNum = [](char32_t InChar)
		{
			return 
				(InChar >= U'a' && InChar <= U'z') || (InChar >= U'A' && InChar <= U'Z')
				|| (InChar >= U'0' && InChar <= U'9');
		};

		//parse into tokens
		CString Error;
		if(InString.IsEmpty())
		{
			Error = U"empty string";
			return Error;
		}

		TVector<SToken>& Tokens =OutTokens;

		int64_t CharIndex=0;
		int64_t LineIndex=0;
		int64_t ColumnIndex=0;

		auto GoToNextChar = [&]()
		{
			assert(CharIndex<InString.GetLength());
			
			char32_t Char = InString[CharIndex];
			if(Char==U'\n')
			{
				LineIndex++;
				ColumnIndex = 0;
			}
			else
			{
				ColumnIndex++;
			}
			CharIndex++;
		};

		while(CharIndex<InString.GetLength())
		{
			if(CharIndex==InString.GetLength())
			{
				//reach end
				break;
			}

			char32_t Char = InString[CharIndex];
			if (IsSpace(Char))
			{
				//skip
				GoToNextChar();
			}
			else if(Char==U'{')
			{
				Tokens.Add(SToken( SToken::EType::LeftBrace,LineIndex,ColumnIndex ));
				GoToNextChar();
			}
			else if (Char == U'}')
			{
				Tokens.Add(SToken( SToken::EType::RightBrace,LineIndex,ColumnIndex ));
				GoToNextChar();
			}
			else if (Char == U'[')
			{
				Tokens.Add(SToken( SToken::EType::LeftBracket,LineIndex,ColumnIndex ));
				GoToNextChar();
			}
			else if (Char == U']')
			{
				Tokens.Add(SToken( SToken::EType::RightBracket,LineIndex,ColumnIndex ));
				GoToNextChar();
			}
			else if (Char == U':')
			{
				Tokens.Add(SToken( SToken::EType::Colon,LineIndex,ColumnIndex ));
				GoToNextChar();
			}
			else if (Char == U',')
			{
				Tokens.Add(SToken( SToken::EType::Comma,LineIndex,ColumnIndex ));
				GoToNextChar();
			}
			else if (Char == U'\"')
			{
				//skip '\"'
				GoToNextChar();

				CString Value;

				//look for next '\"'
				while(true)
				{
					if(CharIndex>=InString.GetLength())
					{
						Error = U"expect a '\"' before reach end";
						return Error;
					}

					Char = InString[CharIndex];
					if(Char == U'\\')
					{
						//this might be a escape char
						//skip '\\'

						GoToNextChar();
						Char = InString[CharIndex];

						if(CharIndex>=InString.GetLength())
						{
							Error = U"expect a '\"' before reach end";
							return Error;
						}

						if (Char == U'"')
						{
							Value+=U'\"';
							GoToNextChar();
							continue;
						}
						else if(Char==U'\\')
						{
							Value+=U'\\';
							GoToNextChar();
							continue;
						}
						else
						{
							Value+=U'\\';
							continue;
						}
					}


					if(Char==U'"')
					{
						//found
						//skip '\"'
						GoToNextChar();
						break;
					}

					Value+=Char;
					GoToNextChar();

				}
				
				Tokens.Add(SToken(Value,LineIndex,ColumnIndex ));
			}
			else if (Char == U't')
			{
				//check if this is a true
				if(CharIndex+3>=InString.GetLength())
				{
					Error = U"expect a 'true' before reach end";
					return Error;
				}

				if(InString[CharIndex+1]==U'r' && InString[CharIndex+2]==U'u' && InString[CharIndex+3]==U'e')
				{
					Tokens.Add(SToken(true,LineIndex,ColumnIndex ));
					
					GoToNextChar();
					GoToNextChar();
					GoToNextChar();
					GoToNextChar();
				}
				else
				{
					Error = WH::Format(U"expect a 'true' at :Line {} Cloumn {}",LineIndex,ColumnIndex);
					return Error;
				}
			}
			else if (Char == U'f')
			{
				//check if this is a false
				if(CharIndex+4>=InString.GetLength())
				{
					Error = U"expect a 'false' before reach end";
					return Error;
				}

				if(InString[CharIndex+1]==U'a' && InString[CharIndex+2]==U'l' && InString[CharIndex+3]==U's' && InString[CharIndex+4]==U'e')
				{
					Tokens.Add(SToken(false,LineIndex,ColumnIndex ));
					
					GoToNextChar();
					GoToNextChar();
					GoToNextChar();
					GoToNextChar();
					GoToNextChar();
				}
				else
				{
					Error = WH::Format(U"expect a 'false' at :Line {} Cloumn {}",LineIndex,ColumnIndex);
					return Error;
				}
			}
			else if (Char == U'n')
			{
				//check if this is a null
				if(CharIndex+3>=InString.GetLength())
				{
					Error = U"expect a 'null' before reach end";
					return Error;
				}

				if(InString[CharIndex+1]==U'u' && InString[CharIndex+2]==U'l' && InString[CharIndex+3]==U'l')
				{
					Tokens.Add(SToken(SToken::EType::Null,LineIndex,ColumnIndex ));
					
					GoToNextChar();
					GoToNextChar();
					GoToNextChar();
					GoToNextChar();
				}
				else
				{
					Error = WH::Format(U"expect a 'null' at :Line {} Cloumn {}",LineIndex,ColumnIndex);
					return Error;
				}
			}
			else if (IsDigit(Char) || Char == U'-')
			{
				//this is a number
				int64_t CurrentLine=LineIndex;
				int64_t CurrentColumn=ColumnIndex;

				int64_t Start = CharIndex;
				int64_t Length = 1;

				//skip first char
				GoToNextChar();

				bool bIsFloat = false;
				while(true)
				{
					if(CharIndex>=InString.GetLength())
					{
						break;
					}

					Char = InString[CharIndex];

					if(IsDigit(Char))
					{
						Length++;
						GoToNextChar();
					}
					else if(Char==U'.')
					{
						if(bIsFloat)
						{
							Error = WH::Format(U"expect a number at :Line {} Cloumn {}",LineIndex,ColumnIndex);
							return Error;
						}
						else
						{
							bIsFloat = true;
							Length++;
							GoToNextChar();
						}
					}
					else
					{
						//end	
						break;
					}
				}
				
				if(bIsFloat)
				{
					Tokens.Add(SToken(CString(InString.SubString(Start,Length)).ToDouble(),CurrentLine,CurrentColumn));
				}
				else
				{
					Tokens.Add(SToken(CString(InString.SubString(Start,Length)).ToInt64(),CurrentLine,CurrentColumn));
				}
			}

			else {
				Error = WH::Format(U"unexpected char '{}' at :Line {} Cloumn {}",Char,LineIndex,ColumnIndex);
				return Error;
			}
		}
	
		return Error;
	}

};